#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_event_connect.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <pcre.h>
#include "dec_making.h"
#include "decs_pub.h"
#include "decs.h"

#define DEC_MAX_PACKET_SIZE 2048
//dec_making_cfg_t dec_making_cfg;


static ngx_int_t 
dec_making_init_module(ngx_cycle_t *cycle);
//static char *
//dec_making_addr(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *
dec_making_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
int dec_making_proc(dec_making_request_t *dmrt);
static ngx_int_t 
dec_making_init_process(ngx_cycle_t *cycle);


static ngx_command_t  dec_making_commands[] = {

    {ngx_string("dec-making"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG|NGX_CONF_BLOCK,
     dec_making_block,
     0,
     0,
     NULL},
    {ngx_string("dec_addr"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_2MORE,
     decs_set_decs_addr,
     0,
     0,
     NULL},
	{ngx_string("dec_log"),
	 NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_2MORE,
	 decs_set_log_addr,
	 0,
	 0,
	NULL},
    {ngx_string("redis"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,
     decs_set_redis_addr,
     0,
     0,
     NULL},
    {ngx_string("cfg_db"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,
     ngx_conf_set_mysql_slot,
     0,
     offsetof(decs_cfg_t, cfg_db),
     NULL},
    {ngx_string("cfg_expire"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_sec_slot,
     0,
     offsetof(decs_cfg_t, cfg_expire),
     NULL},
    {ngx_string("cfg_sync_interval"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_sec_slot,
     0,
     offsetof(decs_cfg_t, cfg_sync_interval),
     NULL},
    {ngx_string("sync_url"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_str_slot,
     0,
     offsetof(decs_cfg_t, sync_url),
     NULL},
    {ngx_string("user_ad_expire"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_sec_slot,
     0,
     offsetof(decs_cfg_t, user_ad_expire),
     NULL},
    {ngx_string("user_ad_interval"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_sec_slot,
     0,
     offsetof(decs_cfg_t, user_ad_interval),
     NULL},
    {ngx_string("sync_domain_url"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_str_slot,
     0,
     offsetof(decs_cfg_t, sync_domain),
     NULL},
    {ngx_string("sync_redirect_url"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_str_slot,
     0,
     offsetof(decs_cfg_t, sync_redirect),
     NULL},
    {ngx_string("sync_delay"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_sec_slot,
     0,
     offsetof(decs_cfg_t, sync_delay),
     NULL},
    {ngx_string("url_ext"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,
     ngx_conf_set_str_array_slot,
     0,
     offsetof(decs_cfg_t, url_ext),
     NULL},
    {ngx_string("cookie_iframe"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     decs_set_cookie_iframe,
     0,
     0,
     NULL},
    {ngx_string("ad_iframe"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     decs_set_ad_iframe,
     0,
     0,
     NULL},
    { ngx_string("ip_allow"),
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
      decs_set_ip_rule,
      0,
      0,
      NULL },
    { ngx_string("ip_deny"),
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
      decs_set_ip_rule,
      0,
      0,
      NULL },
    {ngx_string("ua_allow"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     decs_set_ua_rule,
     0,
     offsetof(decs_cfg_t, ua_rules),
     NULL},
    {ngx_string("ua_deny"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     decs_set_ua_rule,
     0,
     0,
     NULL},
    {ngx_string("domain_list"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     decs_set_domain_list,
     0,
     0,
     NULL},
	{ngx_string("ad_policy_list"),
	 NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
	 decs_set_ad_policy_list,
	 0,
	 0,
	 NULL},
    {ngx_string("redirect_list"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     decs_set_redirect_list,
     0,
     0,
     NULL},
    {ngx_string("cookie_expire"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_sec_slot,
     0,
     offsetof(decs_cfg_t, cookie_expire),
     NULL},
    {ngx_string("cookie_map_interval"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_sec_slot,
     0,
     offsetof(decs_cfg_t, cookie_map_interval),
     NULL},
    {ngx_string("url_tag"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_str_slot,
     0,
     offsetof(decs_cfg_t, url_tag),
     NULL},
    {ngx_string("area"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_str_slot,
     0,
     offsetof(decs_cfg_t, area),
     NULL},
    {ngx_string("httplog"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
     ngx_conf_set_flag_slot,
     0,
     offsetof(decs_cfg_t, httplog_enabled),
     NULL},
    { ngx_string("log_ip_allow"),
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
      decs_set_log_ip_rule,
      0,
      0,
      NULL },
    { ngx_string("log_ip_deny"),
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
      decs_set_log_ip_rule,
      0,
      0,
      NULL },
    {ngx_string("log_ext_allow"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,
     decs_set_log_ext_rule,
     0,
     offsetof(decs_cfg_t, log_ext),
     NULL},
    {ngx_string("log_ext_deny"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,
     decs_set_log_ext_rule,
     0,
     offsetof(decs_cfg_t, log_ext),
     NULL},
    {ngx_string("log_dir"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_str_slot,
     0,
     offsetof(decs_cfg_t, log_dir),
     NULL},
    {ngx_string("log_disk_num"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_num_slot,
     0,
     offsetof(decs_cfg_t, log_disk_num),
     NULL},
    {ngx_string("log_domain"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,
     ngx_conf_set_str_array_slot,
     0,
     offsetof(decs_cfg_t, log_domain),
     NULL},
    {ngx_string("ad"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_flag_slot,
     0,
     offsetof(decs_cfg_t, ad_enabled),
     NULL},
    {ngx_string("cookie_mapping"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_flag_slot,
     0,
     offsetof(decs_cfg_t, cookie_mapping_enabled),
     NULL},
    {ngx_string("access_log"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     decs_set_access_log,
     0,
     0,
     NULL},
    {ngx_string("show_rate"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_num_slot,
     0,
     offsetof(decs_cfg_t, show_rate),
     NULL},
    {ngx_string("click_rate"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_num_slot,
     0,
     offsetof(decs_cfg_t, click_rate),
     NULL},
    {ngx_string("fixed_url_ad"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE2,
     decs_set_fixed_url_ad,
     0,
     0,
     NULL},
    {ngx_string("regex_url_ad"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE2,
     decs_set_regex_url_ad,
     0,
     0,
     NULL},
    {ngx_string("phone_decs_addr"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_2MORE,
     decs_set_phone_decs_addr,
     0,
     0,
     NULL},
    {ngx_string("shared_mem"),
     NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE12,
     decs_set_shared_mem,
     0,
     0,
     NULL},
    ngx_null_command
};


static ngx_core_module_t  dec_making_module_ctx = {
    ngx_string("dec_making"),
    decs_create_conf,
    decs_init_conf
};


ngx_module_t  dec_making_module = {
    NGX_MODULE_V1,
    &dec_making_module_ctx,                /* module context */
    dec_making_commands,                   /* module directives */
    NGX_CORE_MODULE,                       /* module type */
    NULL,                                  /* init master */
    dec_making_init_module,                    /* init module */
    dec_making_init_process,                   /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};

static void
dec_making_recv_from_pps(ngx_event_t *rev)
{
    ngx_connection_t            *c = NULL;
    ssize_t                     n;
    u_char                      buf[1600];
    
    c = rev->data;

    ngx_log_debug(NGX_LOG_DEBUG , ngx_cycle->log, 0,
                   "dec_making_recv_handler");
    
    c->requests++;

    while (rev->ready)
    {
        if (rev->ready) {
            n = c->recv(c, buf, sizeof(buf));
        } else {
            n = NGX_AGAIN;
        }

        if (n == NGX_AGAIN) {
            return;
        }

        if (n == 0) {
            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
                          "client closed connection");
            return;
        }

        if (n == NGX_ERROR) {
            c->error = 1;
            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, 
                "c->recv failed");
            return;
        }
        //sleep(30);
        decs_pkt_handler((char*)buf, (int)n);
    }
    return;
}

static void
dec_making_recv_from_decs(ngx_event_t *rev)
{
    ngx_connection_t            *c = NULL;
    ssize_t                     n;
    u_char                      buf[1600];
    
    c = rev->data;

    ngx_log_debug(NGX_LOG_DEBUG , ngx_cycle->log, 0,
                   "dec_making_recv_handler");
    
    c->requests++;

    while (rev->ready)
    {
        if (rev->ready) {
            n = c->recv(c, buf, sizeof(buf));
        } else {
            n = NGX_AGAIN;
        }

        if (n == NGX_AGAIN) {
            return;
        }

        if (n == 0) {
            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
                          "client closed connection");
            return;
        }

        if (n == NGX_ERROR) {
            c->error = 1;
            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, 
                "c->recv failed");
            return;
        }

        decs_pkt_handler_lb((char*)buf, (int)n);
    }
    return;
}

ngx_int_t
dec_making_add_listening(ngx_conf_t *cf, decs_cfg_t *cfg, decs_t *d)
{
    ngx_listening_t           *ls;
    struct sockaddr_in addr;

    //listening for pps
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(d->ip);
    addr.sin_port = htons(d->port_for_pps);
    
    ls = ngx_create_listening(cf, (struct sockaddr*)&addr, sizeof(addr));
    if (ls == NULL) {
        return NGX_ERROR;
    }

    ls->addr_ntop = 1;

    ls->handler = (void*)dec_making_recv_from_pps;

    ls->pool_size = 256;
    ls->post_accept_timeout = 60000;

    ls->logp = ngx_cycle->log;
    ls->log.data = &ls->addr_text;
    ls->log.handler = ngx_accept_log_error;

    ls->backlog = NGX_LISTEN_BACKLOG;
    ls->rcvbuf = -1; //unset
    ls->sndbuf = -1; //unset
    ls->type = SOCK_DGRAM;
    ls->servers = cfg;

    //listening for other decs
    if (d->port_for_decs == 0)
    {
        return NGX_OK;
    }
    
    addr.sin_port = htons(d->port_for_decs);
    
    ls = ngx_create_listening(cf, (struct sockaddr*)&addr, sizeof(addr));
    if (ls == NULL) {
        return NGX_ERROR;
    }

    ls->addr_ntop = 1;

    ls->handler = (void*)dec_making_recv_from_decs;

    ls->pool_size = 256;
    ls->post_accept_timeout = 60000;

    ls->logp = ngx_cycle->log;
    ls->log.data = &ls->addr_text;
    ls->log.handler = ngx_accept_log_error;

    ls->backlog = NGX_LISTEN_BACKLOG;
    ls->rcvbuf = -1; //unset
    ls->sndbuf = -1; //unset
    ls->type = SOCK_DGRAM;
    ls->servers = cfg;
    
    return NGX_OK;
}


static char *
dec_making_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char                        *rv;
    ngx_conf_t                   pcf;
    ngx_str_t                   *value;
    decs_cfg_t                  *cfg = conf;

    value = cf->args->elts;

    if (ngx_strcmp(value[1].data, "on") == 0)
        cfg->enabled = 1;
    else
        cfg->enabled = 0;
    
    pcf = *cf;
    
    rv = ngx_conf_parse(cf, NULL);

    *cf = pcf;

    return rv;
}

static ngx_int_t 
dec_making_init_module(ngx_cycle_t *cycle)
{
    decs_cfg_t *cfg = (decs_cfg_t *)ngx_get_conf(cycle->conf_ctx, dec_making_module);
    return decs_init_module(cycle, cfg);
}

static ngx_int_t 
dec_making_init_process(ngx_cycle_t *cycle)
{
    decs_cfg = (decs_cfg_t *)ngx_get_conf(cycle->conf_ctx, dec_making_module);
    return NGX_OK;
}

